Basic Extensions to C Preprocessor

The basic extensions to the C Preprocessor generally have functionality that is very similar to the functionality already found in the C Preprocessor, but extends it in some way.

%assign

%assign is similar to the non-functional form of %define, in that it defines text to be substituted for an identifier. Where %assign differs is that the text to be substituted is evaluated as if it were an expression at the time the %assign is encountered. This is useful for example in %rep loops. For example the following code makes a data structure that consists of the integers from 1 to 100, in ascending order:

%assign WORKING 1
%rep 100
    db WORKING
%assign WORKING WORKING + 1
%endrep

But there is another difference with %assign. It is the only preprocessor directive that interacts with data structures in the assembler; so for example the %assign expression can contain expressions involving labels and the program counter. Thus:

helloWorld db "hello world"
%assign COUNTER 64 - ($-helloWorld)
%assign PADDING ($-helloWorld)
%rep COUNTER
    db PADDING
    %assign PADDING PADDING + 1
%endrep

puts the string "hello world" in the text, followed by the byte for 11, the byte for 12, etc... up to the byte for 63.

Note that this latter behavior of interacting with the assembler is only valid if code is being assembled. If the preprocess-only switch is specified on the command line, assembler symbols will not be available, and the latter example will result in errors.

%iassign

%iassign is a form of %assign where the identifier is considered to be case insensitive. So for example:

%iassign COUNTER 63
%rep Counter
    ...
%endrep

and similar case variants on the word counter would still result in a loop that executes 63 times.

%idefine

%idefine is a form of %define where the identifier is assumed to be case insensitive. So for example:

%idefine COUNTER 4
%idefine counter 4
%idefine Counter 4

are equivalent statements, and any case variant of the word COUNTER will match for substitution. Note that this case sensitivity only extends to the identifier; any parameters specified in a function-style %idefine are still case-sensitive for purposes of substitution.

%elifdef

%elifdef is a %elif-style conditional that takes an identifer as an argument. If the identifier has been defined with a previous %define or %assign statement, the next block will be assembled, otherwise the next block will be ignored.

For example:

%define COLOR 3
%if COLOR == 2
    mov eax,4
%elifdef COLOR
    inc eax
%endif

will result in the mov statement being ignored and the inc statement being assembled because COLOR has been defined but is not 2.

Note that a definition declared with %define or %assign must match the argument exactly, whereas a definition declared with %idefine or %iassign can differ in case and still match. %elifdef will not match identifiers declared with %macro or %imacro.

See the section on Conditional Processing for more on %elif-style conditionals.

%elifndef

%elifndef is a %elif-style conditional that takes an identifer as an argument. If the identifier has not been defined with a previous %define or %assign statement, the next block will be assembled, otherwise the next block will be ignored.

For example:

%define COLOR 3
%if COLOR == 2
    mov eax,4
%elifndef COLOR
    inc eax
%endif

will result in nothing being assembled because COLOR is defined but not equal to 2.

Note that a definition declared with %define or %assign can have any difference from the argument and trigger assembly of the block, whereas a definition declared with %idefine or %iassign must differ in some way other than in case. %elifndef will assemble the following block for identifiers declared with %macro or %imacro.

See the section on Conditional Processing for more on %elif-style conditionals.